package org.thomias.exo6.monitor;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.TableItem;

public class AppMonitor implements Runnable {

	public class MonitoredTask {

		public TableEditor te;
		public int lasti;
		private long startTime;
		public Future<?> future;

		public MonitoredTask(TableEditor te, Future<?> future) {
			this.te = te;
			this.future = future;
			this.lasti = 0;
			this.startTime = System.currentTimeMillis();
		}

	}

	private static final long SLEEPINT = 1000;

	Map<Task, MonitoredTask> map;

	private Thread t;
	private Application app;
	private ExecutorService pool;

	public AppMonitor(Application application) {
		this.app = application;
		map = new HashMap<>();
		pool = Executors.newFixedThreadPool(4);
	}

	public void start() {
		t = new Thread(this);
		t.start();
	}

	public void stop() {
		pool.shutdownNow();
		t.interrupt();
	}

	public void monitor(Task task) {
		// add a line in the table
		TableEditor te = new TableEditor(app.table);

		te.grabHorizontal = true;
		te.horizontalAlignment = SWT.LEFT;
		TableItem item = new TableItem(app.table, SWT.NONE);
		item.setData(task);
		ProgressBar pb;
		if (task instanceof MonitorableTask) {
			pb = new ProgressBar(app.table, SWT.HORIZONTAL | SWT.SMOOTH);
			pb.setMinimum(0);
			pb.setMaximum(100);
		} else {
			pb = new ProgressBar(app.table, SWT.HORIZONTAL | SWT.SMOOTH
					| SWT.INDETERMINATE);
		}
		te.setEditor(pb, item, 3);
		Future<?> future = pool.submit(task);
		MonitoredTask mt = new MonitoredTask(te, future);
		map.put(task, mt);
	}

	@Override
	public void run() {

		try {
			while (true) {
				Thread.sleep(SLEEPINT);
				for (Task task : map.keySet()) {
					showProgress(task);
				}
			}
		} catch (InterruptedException e) {
		}

	}

	public void showProgress(final Task task) {
		if (!map.containsKey(task)) {
			return;
		}
		app.getShell().getDisplay().asyncExec(new Runnable() {
			public void run() {
				if (task instanceof MonitorableTask) {
					showMonitorableTask((MonitorableTask) task);
				} else {
					showTask(task);
				}

				if (task.isComplete() || map.get(task).future.isCancelled()) {
					map.get(task).te.getEditor().dispose();
					map.get(task).te.getItem().dispose();
					map.get(task).te.dispose();
					map.remove(task);
					for (MonitoredTask mt : map.values()) {
						mt.te.layout();
					}
				}
			}
		});
	}

	protected void showTask(Task task) {
		long elapsed = (System.currentTimeMillis() - map.get(task).startTime) / 1000;
		String sStarted = "N/A";
		if (task.hasStarted()) {
			long started = (System.currentTimeMillis() - task.startTime) / 1000;
			sStarted = started + "s";
		}

		String sRemaining = "N/A";
		TableItem item = map.get(task).te.getItem();
		if (item.isDisposed()) {
			return;
		}
		item.setText(new String[] { task.name, "10B", "U", "", "N/A",
				elapsed + "s", sStarted, sRemaining });
	}

	protected void showMonitorableTask(MonitorableTask task) {
		long elapsed = (System.currentTimeMillis() - map.get(task).startTime) / 1000;
		String sStarted = "N/A";
		if (task.hasStarted()) {
			long started = (System.currentTimeMillis() - task.startTime) / 1000;
			sStarted = started + "s";
		}
		int left = 100 - task.i;
		double speed = 1000 * ((double) (task.i - map.get(task).lasti))
				/ SLEEPINT;
		map.get(task).lasti = task.i;
		int remaining = (int) (((double) elapsed / (double) task.i) * left);
		String sRemaining = (task.i == 0) ? "N/A" : remaining + "s";
		TableItem item = map.get(task).te.getItem();
		if (item.isDisposed()) {
			return;
		}
		item.setText(new String[] { task.name, "10B", "U", "",
				speed + "progress/s", elapsed + "s", sStarted, sRemaining });
		ProgressBar pb = (ProgressBar) map.get(task).te.getEditor();
		if (pb.isDisposed()) {
			return;
		}
		pb.setSelection(task.i);
	}
}
